Skip to content

test(scripts): give six more self-tests a battery roster and floor (#13799 batch 3) - #15156

Merged
baozhoutao merged 6 commits into
mainfrom
claude/issue-13799-batch3-class1-sink
Sep 4, 2026
Merged

test(scripts): give six more self-tests a battery roster and floor (#13799 batch 3)#15156
baozhoutao merged 6 commits into
mainfrom
claude/issue-13799-batch3-class1-sink

Conversation

@claude

@claude claude Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Part of #13799 (batch 3, the class-1-ONLY deferrals from PR #15003's table).

PR #13487's roster-and-floor shape, exactly as batches 1/1b/1c/2 landed it (PRs #14851, #14872, #14896, #15003), transplanted onto the files whose only recorded defect in #15003's table was the assertion-sink shape while the banner count and the body-level verdict site already qualified. 39 batteries / 306 cases pinned across 6 files. The seventh candidate is deferred with a measured reason rather than forced.

Per file, and nothing else:

  • a module-level frozen SELF_TEST_BATTERIES roster of names with a per-battery floor, SELF_TEST_BATTERY_FLOOR pinning the roster's own size, and UNATTRIBUTED_BATTERY, placed after the import block;
  • battery('BANNER TEXT') at each existing section banner, named verbatim from the banner with its marker stripped;
  • one registerCase() at the head of the one in-body assertion helper this PR gives each file — which is the whole of the class-1 work;
  • the floor evaluated immediately before the existing verdict site, its messages entering the file's own failure sink so the report the file already prints carries them.

⛔ No case is rewritten, none reordered, no assertion changes meaning. ⛔ No TOTAL is pinned.

What "class-1" cost per file, since that was the deferral reason

The three sink shapes in #15003's table needed three different — and each strictly local — repairs:

shape files what this PR did
module-level assert check-adr-links, check-doc-anchors, check-kernel-hook-pairs a thin in-body wrapper check(cond, message) that registers the case and then defers to the existing assert, whose semantics (print, process.exit(1) on the first failure) are untouched. Call sites change by one token: assert( becomes check(.
sink written at inline sites check-published-files (7 sites), ablation-dist-preflight (6 sites) one block-bodied helper replacing the inline writes. Message text is byte-identical; the only behavioural change is that the case is COUNTED whether it holds or not, which is exactly what lets the floor tell "held" from "never ran".
concise-arrow sink t pm/check-clause2-carriers a block body for t, so it can hold registerCase().

In the inline-sink pair the failure-message template is now evaluated eagerly rather than only on the failing branch. Every one of those templates interpolates values already computed on the line above, so this is free of side effects; it is recorded because it is the one place where the diff is not a pure re-routing.

The floor, and case count before == after

Both numbers are measured on a run, not derived from the diff. before for check-clause2-carriers is the count its own verdict line already printed; for ablation-dist-preflight it is the lines the base run prints, one per case; for the other four the base file was instrumented with a counter at the point each case is evaluated, run, and then restored — restore proven by git hash-object equalling the HEAD blob and by an empty git diff HEAD, not by the exit code of the restore command.

file batteries cases before cases after how before was read
scripts/ablation-dist-preflight.mjs 3 (first hoisted) 36 36 lines on the base run
scripts/check-adr-links.mjs 4 23 23 instrumented base assert
scripts/check-doc-anchors.mjs 8 22 22 instrumented base assert
scripts/check-kernel-hook-pairs.mjs 10 16 16 instrumented base assert
scripts/check-published-files.mjs 2 (first hoisted) 59 59 counter at each of the 7 evaluation sites
scripts/pm/check-clause2-carriers.mjs 12 150 150 the verdict line's own count
total 39 306 306

Two files run cases before their first banner (ablation-dist-preflight, check-published-files); their first battery is opened at the top of the body and that banner then carries no second opener — the PR #13487 precedent's own shape, as batches 1b and 2 did it.

check-published-files's 59 reconciles against its own tables independently of the instrument: 21 pattern + 12 classification + 4 population-declaration for the first battery, and 12 exports verdict + 6 (the Fix:-line loop skips the 6 rows expected to pass) + 3 census-floor + 1 shared enumerator for the second.

The card's own instrument agrees

node scripts/measure-self-test-floor.mjs, which the card names as the thing to re-measure with rather than re-derive:

base ebb082204 this branch
unfloored (hole 1) 61 of 167 55 of 167
floored by a roster 104 110

+6, and the six new roster entries are exactly this PR's six files.

Ablation: each floor was made to fail, on the battery it names

Per file: the first declared battery's opener is renamed so that battery registers nothing, the self-test is run, and it must exit non-zero naming that battery. Then restore, and it must be green again. Both legs are proven on disk — the mutation by counting the injected marker, the restore by git hash-object against the HEAD blob plus an empty git diff HEAD and a zero residual-marker count — never by an editor's or a git checkout's exit code, and every script carries a trap ... EXIT INT TERM restore.

These are .mjs scripts executed directly from source: no dist/ and no exports resolution stands between the edit and the run, so the rebuild leg the ablation contract requires for package-resolved subjects does not apply here. Recorded rather than skipped silently.

file ablated battery ablated exit named it restored exit
ablation-dist-preflight.mjs whole-tree accounting: the pure table 1 ✓ DID NOT RUN 0
check-adr-links.mjs Discrimination: verbatim link shapes are invisible; prose ones are not. 1 ✓ DID NOT RUN 0
check-doc-anchors.mjs Slug parity with the renderer, on the two shapes that actually broke. 1 ✓ DID NOT RUN 0
check-kernel-hook-pairs.mjs A hook pinned on both sides passes. 1 ✓ DID NOT RUN 0
check-published-files.mjs the dispatch-gates declaration (#10542) 1 ✓ DID NOT RUN 0
pm/check-clause2-carriers.mjs the declaration reader: the fixed spelling, and everything that is not 1 ✓ DID NOT RUN 0

⭐ The one behavioural hazard #15003 measured, measured again here

#15003 deferred scripts/pm/dispatch-gates.mjs because its self-test reads its own source and four battery names carrying path-shaped text were read by its own path-population extractor as declared populations — a change of meaning, not a no-op.

This batch adds new string literals to six files that dispatch-gates reads as gate sources, so the same extractor could have read a battery name as a watch hint. It does not. extractWatchHints was run over all six files on both trees:

file hints before hints after
ablation-dist-preflight.mjs [] []
check-adr-links.mjs ["docs/adr"] ["docs/adr"]
check-doc-anchors.mjs ["content/**","README.md/**","ARCHITECTURE.md/**"] identical
check-kernel-hook-pairs.mjs ["packages/core/src"] ["packages/core/src"]
check-published-files.mjs 13 hints identical, same order
pm/check-clause2-carriers.mjs [] []

Byte-identical on every file. check:pm-dispatch-gates (1343 cases) and check:declared-population-live are both green on the branch, which is the same finding reached the other way.

Deferred (1), with the measurement

file measured reason
scripts/partition-test-shards.mjs shape ✓ on two of three, sink ✗ — and the class-1 repair here is not a re-routing. 7 named banners and a body-level verdict site, as #15003 recorded. But its 36 assertions are bare throw at every site, and only 22 are the uniform single-line if (cond) throw new Error(msg);. The other 14 are multi-line, several nested inside for loops and closures. Routing them through a helper means inverting the failure condition by hand at each site — and a mis-scoped or dropped ! yields an assertion that still registers its case, still prints nothing, and still passes, so the floor being installed cannot see it. That is a rewrite of every assertion rather than the "route without changing assertion semantics" the shape asks for, so it is deferred rather than improvised past. The route a later batch can take without inverting anything: a thunk helper, const check = (fn) => { registerCase(); fn(); };, wrapping each site's existing if (...) throw ... verbatim — no condition is touched, at the cost of reindenting the 14 multi-line blocks.

⛔ Untouched by construction, per the dispatch: scripts/pm/dispatch-gates.mjs (still DEFERRED, reason above), scripts/pm/check-half-states.mjs (~17 kloc, the analyzer cannot resolve its body), every class-2 / class-3 file in #15003's table, and the files held by other live cards.

Shrink-only ledgers: scanned, one worth naming

Every battery whose cases come off a module-level list was enumerated and each list read, per the card's "a floor is not always the measured count":

  • CONVENTION_ILLUSTRATIVE_TARGETS (check-adr-links, 3 rows feeding 6 of battery 3's 8 cases) is a pin of three strings that ADR-0046 must keep containing — the file's own header says so. It grows or holds; it is not a baseline that legitimate work shortens. Named here because it is the one floor in this batch that moves with a list at all.
  • KNOWN_DEAD_TARGETS (check-adr-links) is shrink-only, and is exactly the case the card warns about — so note what it drives: one structural case (no entry has outlived its excuse), not one per row. It is currently [], and shrinking it cannot move that count. This is the card's own remedy — pin the part that does not move with the list — arrived at without needing to deviate.
  • ROOT_DIR_WATCH_HINTS, FORBIDDEN, EXPORTS_CENSUS_FLOOR (check-published-files) and CLAUSE2_VALUES (check-clause2-carriers) are registers and closed sets that grow or are pinned by an adjacent assertion; none is shrink-only.

Verification

Every exit code captured by redirect before any pipe; each gate's own verdict line read rather than a bare $?. Run on 6bddfc959, the final commit of this branch.

  • Family derived from the tree, not from a hand-written diff: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack → 27 commands, all 27 run, all exit 0.
  • The always-runs tail: check:pm-dispatch-gates (exit 0, 5m39s, 1343 cases), check-self-test-wired + --self-test, check-self-test-workflow-commands + --self-test, check:declared-population-live, check:ratchet-remedy-authority, check:nul-bytes — all exit 0.
  • Each of the six in both modes: its --self-test and its normal gate run against the live tree. check-adr-links (599 destinations resolve) and check-doc-anchors (298 fragment links across 409 sources) are lint.yml gates and are green on the tree. ablation-dist-preflight is a per-invocation tool, not a repo sweep: with no arguments it prints usage and exits 2, unchanged from the branch point.
  • Neither of the two files that name an edited script (packages/qa/dogfood/test/multi-package-artifact.dogfood.test.ts, packages/plugins/plugin-auth/src/rate-limit-storage-isolation.test.ts) exercises it — both are prose references in comments. Each script's --self-test is its own suite, and all six ran.
  • Control-byte scan over all six edited files (grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]'): no match. check:nul-bytes green (8238 files scanned).

skip-changeset: scripts/** publishes from no package, the same call PR #15003 made for the identical surface.


🤖 Generated with Claude Code

https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk


Generated by Claude Code

Part of #13799 (batch 3). The concise-arrow sink `t` gets a block body so
it can call registerCase(); 12 batteries / 150 cases pinned. Case count
before == after (150), measured against the branch point.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 4, 2026
@github-actions github-actions Bot added the size/m label Sep 4, 2026
Part of #13799 (batch 3). The module-level `assert` keeps its semantics
(print and exit 1 on the first failure); a thin in-body `check()` wrapper
attributes each case to the open battery and defers to it. 4 batteries /
23 cases pinned, and the floor reports through that same `assert`.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
Part of #13799 (batch 3). Same thin in-body `check()` wrapper over the
module-level `assert`; 8 batteries / 22 cases pinned. Gate mode still
green on the tree (298 fragment links across 409 sources).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
Part of #13799 (batch 3). Third of the module-level-`assert` trio, same
thin in-body wrapper; 10 batteries / 16 cases pinned. Its verdict line
prints a LITERAL "10 cases" that tracks nothing and undercounts the 16
assertions the body actually runs — left as found (the card's scope is
the floor, and the handshake stays exactly as landed), recorded instead.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
@github-actions github-actions Bot added size/l and removed size/m labels Sep 4, 2026
Part of #13799 (batch 3). The 7 inline `failures.push(...)` sites now
route through one block-bodied `expect()` that registers the case whether
it holds or not; message text is unchanged. 2 batteries / 59 cases, the
first hoisted because cases run before the first banner. The count
reconciles against the tables: 21 + 12 + 4 and 12 + 6 + 3 + 1.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
Part of #13799 (batch 3). The 6 inline `failed += 1` sites route through
one block-bodied `check()` that keeps both the ✓ and ✗ branches verbatim
and counts the case either way. 3 batteries / 36 cases, the first hoisted
(10 + 3 + 12 + 1 run before the first banner). Not a repo sweep: with no
args it prints usage and exits 2, unchanged from the branch point.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
@claude claude Bot changed the title test(scripts): give seven more self-tests a battery roster and floor (#13799 batch 3) test(scripts): give six more self-tests a battery roster and floor (#13799 batch 3) Sep 4, 2026
@baozhoutao
baozhoutao marked this pull request as ready for review September 4, 2026 04:22
@baozhoutao
baozhoutao enabled auto-merge September 4, 2026 04:22
@baozhoutao
baozhoutao added this pull request to the merge queue Sep 4, 2026
Merged via the queue into main with commit e293038 Sep 4, 2026
37 of 38 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-13799-batch3-class1-sink branch September 4, 2026 04:58
baozhoutao pushed a commit that referenced this pull request Sep 4, 2026
…y roster

`cases` with no failing entry was this self-test's only success condition, so
"every case held" and "the cases never ran" printed the same line.

Batch 6b of the roster-floor transplant, two shapes at once:

- the sink repair PR #15156 landed: the concise arrow `const t = (name, ok,
  detail) => cases.push(...)` gains a block body and registers the case before
  recording it. `cases.push` receives exactly the arguments it always did --
  no case is rewritten, reordered or re-judged.
- the single hoisted battery PR #15217 landed: this file carries no named
  section banner, so it declares ONE battery opened at the top of the
  self-test body, floor at the measured count (12), and pins the roster's own
  size at 1. No comment is promoted to a section head.

A breach files into the self-test's own `cases` sink, so the existing verdict
reds on it with no verdict line rewritten; the #13798 handshake is untouched.

Cases before == after: 12 == 12, measured on runs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
baozhoutao pushed a commit that referenced this pull request Sep 4, 2026
…-test

`cases.filter((c) => !c.cond)` was this self-test's only success condition, so
"every case held" and "the cases never ran" printed the same line (#13489).

Class-1 sink repair, the PR #15156 shape: the concise-arrow `ok` sink gains a
block body that calls `registerCase()` before the unchanged `cases.push`. No
assertion condition is inverted or rewritten.

Class-2 roster, the PR #15217 shape: ONE battery hoisted to the top of the
self-test body, floor at the measured 23, `SELF_TEST_BATTERIES` size pinned at
1. No comment was promoted to a section head.

Case count before == after, measured by pinning the roster to an unreachable
value and reading the breach line: 23. `--self-test` stdout and stderr are
byte-identical to the base tree's, exit 0 on both.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
baozhoutao pushed a commit that referenced this pull request Sep 4, 2026
…attery roster

`failed === 0` was this self-test's only success condition, so "every case
held" and "the cases never ran" printed the same line.

Batch 6b, two shapes at once:

- the sink repair PR #15156 landed: the 12 inline `cases.push({...})` sites
  route through ONE block-bodied in-body helper, `addCase`, which registers
  the case and then performs the identical push. The case object is passed
  through untouched and the loop that runs the cases is not touched at all --
  no case is rewritten, reordered or re-judged.
- the single hoisted battery PR #15217 landed: this file carries no named
  section banner (its `--- n/m: ... ---` comments label fixtures, not
  sections), so it declares ONE battery opened at the top of the self-test
  body, floor at the measured count (12), roster size pinned at 1. No comment
  is promoted to a section head.

A breach files into the self-test's own `failed` counter, so the existing
verdict reds on it with no verdict line rewritten; the #13798 handshake is
untouched. Normal mode stays green on the tree.

Cases before == after: 12 == 12, measured on runs.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
baozhoutao pushed a commit that referenced this pull request Sep 4, 2026
`cases.filter((c) => !c.ok)` was this self-test's only success condition, so
"every case held" and "the cases never ran" printed the same line (#13489).

Class-1 sink repair, the PR #15156 shape: the concise-arrow `t` sink gains a
block body that calls `registerCase()` before the unchanged `cases.push`. No
assertion condition is inverted or rewritten.

Class-2 roster, the PR #15217 shape: ONE battery hoisted to the top of the
self-test body, floor at the measured 20, `SELF_TEST_BATTERIES` size pinned at
1. The file's single named section banner is NOT split on, and no comment was
promoted to a section head.

Case count before == after, measured by pinning the roster to an unreachable
value and reading the breach line: 20 — which agrees with the count the
existing verdict line prints. `--self-test` stdout and stderr are
byte-identical to the base tree's, exit 0 on both.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
baozhoutao pushed a commit that referenced this pull request Sep 4, 2026
The b5 recipe (PR #15217) applied to the remaining eight census rows of
#13799 batch 8a. Each file gets ONE battery opened at the top of its
self-test body, named `BASENAME self-test`, with the floor read off a run
(the roster pinned at 9999 first, the breach line naming N), the roster's own
size pinned at 1, `registerCase()` called from the block body of the helper
the file already has, and a verdict refusing below-floor / DID NOT RUN /
undeclared batteries through the file's own failure sink.

Floors measured on a run: check-adr-symbol-anchors 17, symbol-anchors 51,
check-i18n-walk-parity 23, check-test-completeness 67, checklist-select 17,
release-rehearsal-clone 32, render-release-coverage-anchor 10,
run-with-stall-guard 41.

Two of them are the census's own warning coming true: symbol-anchors has 40
static `assert(` sites but registers 51 (loops), and
render-release-coverage-anchor has 8 static `expect(` sites but registers 10.
A floor counted from the source would have been wrong in both.

Where the helper is module-level (check-adr-symbol-anchors, symbol-anchors,
render-release-coverage-anchor) the case sites call a thin in-body wrapper
that registers and then defers to the existing assertion, exactly as PR
#15156 landed for that shape; no assertion condition is touched.

checklist-select also stops transcribing its case count: the success line's
hand-typed `17` is now read off a counter (#15305). It renders the same text
today, which is what makes the byte comparison across the change readable.

check-test-completeness scopes its floor to the loud run, because
`selfTest({ quiet: true })` also runs on every production invocation of that
gate, where nothing claims a self-test verdict.

Part of #13799
Fixes #15305

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/l skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants